package com.derbysoft.lowcostadapter.dswitch2.translator.impl;

import com.derbysoft.lowcostadapter.commons.dto.PropertyResult;
import com.derbysoft.lowcostadapter.commons.dto.RoomType;
import com.derbysoft.lowcostadapter.commons.dto.SearchResponse;
import com.derbysoft.lowcostadapter.commons.dto.SearchResult;
import com.derbysoft.lowcostadapter.commons.utils.DateTimeUtils;
import com.derbysoft.lowcostadapter.dswitch2.service.helper.Constant;
import com.derbysoft.lowcostadapter.dswitch2.translator.Translator;
import com.derbysoft.remote.dswitch.core.GenericRQ;
import com.derbysoft.remote.dswitch.core.GenericRS;
import com.derbysoft.remote.dswitch.core.ResponseHeader;
import com.derbysoft.remote.dswitch.core.SuccessDTO;
import com.derbysoft.remote.dswitch.generic.constants.UniqueIDType;
import com.derbysoft.remote.dswitch.generic.dto.SimpleAmountDTO;
import com.derbysoft.remote.dswitch.generic.dto.UniqueIDDTO;
import com.derbysoft.remote.dswitch.hotel.constants.FreeMealUnit;
import com.derbysoft.remote.dswitch.hotel.constants.MealsIncludedType;
import com.derbysoft.remote.dswitch.hotel.constants.PaymentType;
import com.derbysoft.remote.dswitch.hotel.constants.RateType;
import com.derbysoft.remote.dswitch.hotel.dto.*;
import com.derbysoft.remote.share.Currency;
import com.derbysoft.remote.share.LanguageType;
import com.derbysoft.remote.share.dto.DateRangeDTO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component("ds2HotelAvailRSTranslator")
public class HotelAvailRSTranslatorImpl implements Translator<SearchResponse, GenericRS<HotelAvailRS>, GenericRQ<HotelAvailRQ>> {

    @Override
    public GenericRS<HotelAvailRS> translate(SearchResponse searchResponse, GenericRQ<HotelAvailRQ> rq) {
        if (StringUtils.isNotBlank(searchResponse.getSearchResult().getReturnStatus().getException())) {
            return TranslateHelper.buildFailedResponse(searchResponse.getSearchResult().getReturnStatus().getException(), rq);
        }
        GenericRS<HotelAvailRS> rs = new GenericRS<HotelAvailRS>();
        rs.setPayload(createHotelAvailRS(searchResponse.getSearchResult(), rq.getPayload().getAvailCriteria()));
        rs.setSuccess(new SuccessDTO());
        rs.setHeader(createHeader(rq));
        return rs;
    }

    private HotelAvailRS createHotelAvailRS(SearchResult searchResult, HotelAvailCriteriaDTO hotelAvailCriteriaDTO) {
        HotelAvailRS hotelAvailRS = new HotelAvailRS();
        hotelAvailRS.setHotelAvailRoomStay(createHotelAvailRoomStayDTO(searchResult, hotelAvailCriteriaDTO));
        return hotelAvailRS;
    }

    private HotelAvailRoomStayDTO createHotelAvailRoomStayDTO(SearchResult searchResult, HotelAvailCriteriaDTO hotelAvailCriteriaDTO) {
        HotelAvailRoomStayDTO hotelAvailRoomStayDTO = new HotelAvailRoomStayDTO();
        hotelAvailRoomStayDTO.setHotelRef(hotelAvailCriteriaDTO.getHotelRef());

        if (searchResult.getPropertyResults() != null &&
                CollectionUtils.isNotEmpty(searchResult.getPropertyResults().getPropertyResult())) {

            PropertyResult propertyResult = searchResult.getPropertyResults().getPropertyResult().get(0);
            hotelAvailRoomStayDTO.setRoomStays(
                    createRoomStays(propertyResult, hotelAvailCriteriaDTO.getStayDateRange(),
                            hotelAvailCriteriaDTO.getRoomStayCandidates().getNumberOfUnits()));
        }

        return hotelAvailRoomStayDTO;
    }

    private List<AvailRoomStayDTO> createRoomStays(PropertyResult propertyResult,
                                                   StayDateRangeDTO stayDateRange, int numberOfUnit) {
        ArrayList<AvailRoomStayDTO> availRoomStayDTOs = new ArrayList<AvailRoomStayDTO>();
        if (propertyResult.getRoomTypes() != null && CollectionUtils.isNotEmpty(propertyResult.getRoomTypes().getRoomType())) {
            Map<String, List<RoomType>> roomTypeGroup = groupByRoomType(propertyResult.getRoomTypes().getRoomType());
            for (List<RoomType> roomTypes : roomTypeGroup.values()) {
                if (roomTypes.size() >= numberOfUnit) {
                    availRoomStayDTOs.add(createAvailRoomStayDTO(roomTypes.get(0), stayDateRange));
                }
            }
        }
        return availRoomStayDTOs;
    }

    private Map<String, List<RoomType>> groupByRoomType(List<RoomType> roomTypes) {
        HashMap<String, List<RoomType>> result = new HashMap<String, List<RoomType>>();
        for (RoomType roomType : roomTypes) {
            String key = generatorRoomTypeKey(roomType);
            List<RoomType> roomTypesGroup = result.get(key);
            if (roomTypesGroup == null) {
                roomTypesGroup = new ArrayList<RoomType>();
                result.put(key, roomTypesGroup);
            }

            roomTypesGroup.add(roomType);
        }
        return result;
    }

    private String generatorRoomTypeKey(RoomType roomType) {
        return String.format("%d`%d", roomType.getMealBasisID(), roomType.getPropertyRoomTypeID());
    }

    protected ResponseHeader createHeader(GenericRQ<HotelAvailRQ> rq) {
        ResponseHeader responseHeader = new ResponseHeader();
        responseHeader.setTaskId(rq.getHeader().getTaskId());
        return responseHeader;
    }

    private AvailRoomStayDTO createAvailRoomStayDTO(RoomType roomType, StayDateRangeDTO stayDateRangeDTO) {
        AvailRoomStayDTO availRoomStayDTO = new AvailRoomStayDTO();
        availRoomStayDTO.setRoomType(createRoomType(roomType));
        availRoomStayDTO.setRatePlan(createRatePlan(roomType));
        availRoomStayDTO.setRoomRate(createRoomRate(roomType, stayDateRangeDTO));
        availRoomStayDTO.setLanguageType(LanguageType.EN_US);
        availRoomStayDTO.setProviderChain(createProviderChain());
        return availRoomStayDTO;
    }

    private ProviderChainDTO createProviderChain() {
        ProviderChainDTO providerChainDTO = new ProviderChainDTO();
        ArrayList<UniqueIDDTO> providers = new ArrayList<UniqueIDDTO>();
        providers.add(new UniqueIDDTO(Constant.LOW_COST_PASSPORT, UniqueIDType.WHOLESALER));
        providerChainDTO.setProviders(providers);
        return providerChainDTO;
    }

    private RoomRateDTO createRoomRate(RoomType roomType, StayDateRangeDTO stayDateRangeDTO) {
        RoomRateDTO roomRateDTO = new RoomRateDTO();
        roomRateDTO.setRates(createRates(roomType, stayDateRangeDTO));
        return roomRateDTO;
    }

    private List<RateDTO> createRates(RoomType roomType, StayDateRangeDTO stayDateRangeDTO) {
        ArrayList<RateDTO> rateDTOs = new ArrayList<RateDTO>();
        rateDTOs.add(createRate(roomType, stayDateRangeDTO));
        return rateDTOs;
    }

    private RateDTO createRate(RoomType roomType, StayDateRangeDTO stayDateRangeDTO) {
        RateDTO rateDTO = new RateDTO();
        DateRangeDTO dateRangeDTO = createDateRange(stayDateRangeDTO);
        rateDTO.setDateRange(dateRangeDTO);
        rateDTO.setPureAmount(createPureAmount(roomType, stayDateRangeDTO));
        rateDTO.setMealsIncluded(createMealsIncluded());
        return rateDTO;
    }

    private MealsIncludedDTO createMealsIncluded() {
        MealsIncludedDTO mealsIncludedDTO = new MealsIncludedDTO();
        mealsIncludedDTO.setMealsIncludedType(MealsIncludedType.UNKNOWN);
        return mealsIncludedDTO;
    }

    private SimpleAmountDTO createPureAmount(RoomType roomType, StayDateRangeDTO stayDateRangeDTO) {
        SimpleAmountDTO simpleAmountDTO = new SimpleAmountDTO();
        BigDecimal duration = new BigDecimal(stayDateRangeDTO.lengthOfStay());
        simpleAmountDTO.setAmount(roomType.getTotal().divide(duration, BigDecimal.ROUND_UP));
        simpleAmountDTO.setCurrency(Currency.GBP);
        return simpleAmountDTO;
    }

    private DateRangeDTO createDateRange(StayDateRangeDTO stayDateRangeDTO) {
        DateRangeDTO dateRangeDTO = new DateRangeDTO();
        dateRangeDTO.setStart(stayDateRangeDTO.getCheckInDate());
        dateRangeDTO.setEnd(DateTimeUtils.offsetDays(stayDateRangeDTO.getCheckOutDate(), -1));
        return dateRangeDTO;
    }

    private RatePlanDTO createRatePlan(RoomType roomType) {
        RatePlanDTO ratePlanDTO = new RatePlanDTO();
        ratePlanDTO.setCode(String.valueOf(roomType.getMealBasisID()));
        ratePlanDTO.setName(roomType.getMealBasis());
        ratePlanDTO.setFreeMeal(createFreeMeal(roomType.getMealBasisID()));
        ratePlanDTO.setRateType(RateType.PREPAY_NET_RATE);
        ratePlanDTO.setPaymentType(PaymentType.PREPAY);
        return ratePlanDTO;
    }

    private FreeMealDTO createFreeMeal(int mealBasisID) {
        FreeMealDTO freeMealDTO = new FreeMealDTO();
        MealBasis mealBasis = MealBasis.fromID(mealBasisID);
        freeMealDTO.setType(mealBasis.getFreeMealType());
        freeMealDTO.setUnit(FreeMealUnit.PER_PERSON);
        freeMealDTO.setDescription(mealBasis.getDescription());
        return freeMealDTO;
    }

    private RoomTypeDTO createRoomType(RoomType roomType) {
        RoomTypeDTO roomTypeDTO = new RoomTypeDTO();

        roomTypeDTO.setCode(String.valueOf(roomType.getPropertyRoomTypeID()));
        roomTypeDTO.setName(roomType.getRoomType());

        return roomTypeDTO;
    }
}
